
#ifndef __STRINGCONV_INCLUDE__H__
#define __STRINGCONV_INCLUDE__H__

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

namespace WCMM{

inline UINT WINAPI GetConversionACP() throw()
{
	return CP_THREAD_ACP;
}

template <class _CharType>
inline bool ConvAllocMemory(
	_Inout_ _Outptr_result_buffer_(nLength) _CharType** ppBuff,
	_In_ int nLength,
	_In_reads_(nFixedBufferLength) _CharType* pszFixedBuffer,
	_In_ int nFixedBufferLength)
{
	if (!ppBuff || nLength < 0 || !pszFixedBuffer){
		return false;
	}

	//
	// if buffer malloced, try to realloc.
	//

	if (*ppBuff != pszFixedBuffer){
		if(nLength > nFixedBufferLength){
			_CharType* ppReallocBuf = static_cast< _CharType* >( _recalloc(*ppBuff, nLength, sizeof( _CharType ) ) );
			if (ppReallocBuf == NULL) {
				return false;
			}
			*ppBuff = ppReallocBuf;
		}else{
			free(*ppBuff);
			*ppBuff=pszFixedBuffer;
		}

	}else{

		//
		// Buffer is not currently malloced.
		//

		if( nLength > nFixedBufferLength ){
			*ppBuff = static_cast< _CharType* >(calloc(nLength, sizeof( _CharType )));
		}else{			
			*ppBuff = pszFixedBuffer;
		}
	}

	if (*ppBuff == NULL)
		return false;

	return true;
		
}

template <class _CharType>
inline void ConvFreeMemory(
	_Pre_maybenull_ _Post_invalid_ _CharType* pBuff,
	_Pre_notnull_ _Pre_writable_size_(nFixedBufferLength) _CharType* pszFixedBuffer,
	_In_ int nFixedBufferLength)
{
	if (!pBuff){
		return;
	}

	if( pBuff != pszFixedBuffer ){
		free( pBuff );
	} 	
}

template< int t_nBufferLength = 128 >
class CW2WEX
{
public:
	CW2WEX(_In_z_ LPCWSTR psz) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz );
	}
	CW2WEX(
		_In_z_ LPCWSTR psz, 
		_In_ UINT nCodePage) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz );
	}

	~CW2WEX() throw()
	{
		ConvFreeMemory(m_psz, m_szBuffer, t_nBufferLength);
	}

	_Ret_z_ operator LPWSTR() const throw()
	{
		return( m_psz );
	}

private:
	void Init(_In_z_ LPCWSTR psz) throw(...)
	{
		if (psz == NULL){
			m_psz = NULL;
			return;
		}

		int nLength = static_cast<int>(wcslen( psz )) + 1;
		ConvAllocMemory(&m_psz,nLength,m_szBuffer,t_nBufferLength);
		ASSERTE(m_psz != NULL);
		CopyMemory( m_psz, psz, nLength*sizeof( wchar_t ));
	}

public:
	LPWSTR m_psz;
	wchar_t m_szBuffer[t_nBufferLength];

private:
	CW2WEX(_In_ const CW2WEX&) throw();
	CW2WEX& operator=(_In_ const CW2WEX&) throw();
};
typedef CW2WEX<> CW2W;

template< int t_nBufferLength = 128 >
class CA2AEX
{
public:
	CA2AEX(_In_z_ LPCSTR psz) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz );
	}
	CA2AEX( _In_z_ LPCSTR psz, _In_ UINT nCodePage) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz );
	}
	~CA2AEX() throw()
	{
		ConvFreeMemory(m_psz,m_szBuffer,t_nBufferLength);
	}

	_Ret_z_ operator LPSTR() const throw()
	{
		return( m_psz );
	}

private:
	void Init(_In_z_ LPCSTR psz) throw(...)
	{
		if (psz == NULL){
			m_psz = NULL;
			return;
		}
		int nLength = static_cast<int>(strlen( psz ))+1;
		ConvAllocMemory(&m_psz,nLength,m_szBuffer,t_nBufferLength);		
		CopyMemory( m_psz, psz, nLength*sizeof( char ));
	}

public:
	LPSTR m_psz;
	char m_szBuffer[t_nBufferLength];

private:
	CA2AEX(_In_ const CA2AEX&) throw();
	CA2AEX& operator=(_In_ const CA2AEX&) throw();
};
typedef CA2AEX<> CA2A;

template< int t_nBufferLength = 128 >
class CA2CAEX
{
public:
	CA2CAEX(_In_z_ LPCSTR psz) throw(...) : m_psz( psz ){}
	CA2CAEX(_In_z_ LPCSTR psz, _In_ UINT nCodePage) throw(...) : m_psz( psz ){}
	~CA2CAEX() throw(){}

	_Ret_z_ operator LPCSTR() const throw()
	{
		return m_psz;
	}

public:
	LPCSTR m_psz;

private:
	CA2CAEX(_In_ const CA2CAEX&) throw();
	CA2CAEX& operator=(_In_ const CA2CAEX&) throw();
};
typedef CA2CAEX<> CA2CA;

template< int t_nBufferLength = 128 >
class CW2CWEX
{
public:
	CW2CWEX(_In_z_ LPCWSTR psz) throw(...) : m_psz( psz ){}
	CW2CWEX(_In_z_ LPCWSTR psz, _In_ UINT nCodePage) throw(...) : m_psz( psz ){}
	~CW2CWEX() throw(){}

	_Ret_z_ operator LPCWSTR() const throw()
	{
		return m_psz;
	}

public:
	LPCWSTR m_psz;

private:
	CW2CWEX(_In_ const CW2CWEX&) throw();
	CW2CWEX& operator=(_In_ const CW2CWEX&) throw();
};
typedef CW2CWEX<> CW2CW;

template< int t_nBufferLength = 128 >
class CA2WEX
{
public:
	CA2WEX(_In_z_ LPCSTR psz) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz, GetConversionACP() );
	}

	CA2WEX(_In_z_ LPCSTR psz, _In_ UINT nCodePage) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz, nCodePage );
	}

	~CA2WEX() throw()
	{
		ConvFreeMemory(m_psz,m_szBuffer,t_nBufferLength);
	}

	_Ret_z_ operator LPWSTR() const throw()
	{
		return( m_psz );
	}

private:
	void Init(
		_In_z_ LPCSTR psz, 
		_In_ UINT nCodePage) throw(...)
	{
		if (psz == NULL){
			m_psz = NULL;
			return;
		}

		int nLengthA = static_cast<int>(strlen( psz ))+1;
		int nLengthW = nLengthA;

		ConvAllocMemory(&m_psz,nLengthW,m_szBuffer,t_nBufferLength);

		BOOL bFailed=(0 == ::MultiByteToWideChar( nCodePage, 0, psz, nLengthA, m_psz, nLengthW ) );
		if (bFailed){
			if (GetLastError()==ERROR_INSUFFICIENT_BUFFER){
				nLengthW = ::MultiByteToWideChar( nCodePage, 0, psz, nLengthA, NULL, 0);
				ConvAllocMemory(&m_psz,nLengthW,m_szBuffer,t_nBufferLength);
				bFailed = (0 == ::MultiByteToWideChar( nCodePage, 0, psz, nLengthA, m_psz, nLengthW ) );
			}			
		}
		if (bFailed){
			throw GetLastError();
		}		
	}

public:
	LPWSTR m_psz;
	wchar_t m_szBuffer[t_nBufferLength];

private:
	CA2WEX(_In_ const CA2WEX&) throw();
	CA2WEX& operator=(_In_ const CA2WEX&) throw();
};
typedef CA2WEX<> CA2W;

template< int t_nBufferLength = 128 >
class CW2AEX
{
public:
	CW2AEX(_In_z_ LPCWSTR psz) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz, GetConversionACP() );
	}

	CW2AEX(_In_z_ LPCWSTR psz, _In_ UINT nCodePage) throw(...) : m_psz( m_szBuffer )
	{
		Init( psz, nCodePage );
	}

	~CW2AEX() throw()
	{		
		ConvFreeMemory(m_psz,m_szBuffer,t_nBufferLength);
	}

	_Ret_z_ operator LPSTR() const throw()
	{
		return( m_psz );
	}

private:
	void Init(_In_z_ LPCWSTR psz, _In_ UINT nConvertCodePage) throw(...)
	{
		if (psz == NULL){
			m_psz = NULL;
			return;
		}

		int nLengthW = static_cast<int>(wcslen( psz ))+1;
		int nLengthA = nLengthW*4;

		ConvAllocMemory(&m_psz,nLengthA,m_szBuffer,t_nBufferLength);

		BOOL bFailed=(0 == ::WideCharToMultiByte( nConvertCodePage, 0, psz, nLengthW, m_psz, nLengthA, NULL, NULL ));
		if (bFailed){
			if (GetLastError()==ERROR_INSUFFICIENT_BUFFER){
				nLengthA = ::WideCharToMultiByte( nConvertCodePage, 0, psz, nLengthW, NULL, 0, NULL, NULL );
				ConvAllocMemory(&m_psz,nLengthA,m_szBuffer,t_nBufferLength);
				bFailed=(0 == ::WideCharToMultiByte( nConvertCodePage, 0, psz, nLengthW, m_psz, nLengthA, NULL, NULL ));
			}			
		}
		if (bFailed){
			throw GetLastError();
		}
	}

public:
	LPSTR m_psz;
	char m_szBuffer[t_nBufferLength];

private:
	CW2AEX(_In_ const CW2AEX&) throw();
	CW2AEX& operator=(_In_ const CW2AEX&) throw();
};
typedef CW2AEX<> CW2A;

#ifdef _UNICODE

#define CW2T CW2W
#define CW2TEX CW2WEX
#define CW2CT CW2CW
#define CW2CTEX CW2CWEX
#define CT2W CW2W
#define CT2WEX CW2WEX
#define CT2CW CW2CW
#define CT2CWEX CW2CWEX

#define CA2T CA2W
#define CA2TEX CA2WEX
#define CA2CT CA2W
#define CA2CTEX CA2WEX
#define CT2A CW2A
#define CT2AEX CW2AEX
#define CT2CA CW2A
#define CT2CAEX CW2AEX

#else  // !_UNICODE

#define CW2T CW2A
#define CW2TEX CW2AEX
#define CW2CT CW2A
#define CW2CTEX CW2AEX
#define CT2W CA2W
#define CT2WEX CA2WEX
#define CT2CW CA2W
#define CT2CWEX CA2WEX

#define CA2T CA2A
#define CA2TEX CA2AEX
#define CA2CT CA2CA
#define CA2CTEX CA2CAEX
#define CT2A CA2A
#define CT2AEX CA2AEX
#define CT2CA CA2CA
#define CT2CAEX CA2CAEX

#endif

}

#endif